/***************************************************************************\
 * The Mattise Kernel														*
 * Copyright 2007 Matthew Iselin											*
 * Licensed under the GPL													*
 *																			*
 * winman.cc																*
 *																			*
 * The Mattise Window Manager - handles window creation, deletion, and		*
 * message handling															*
 *																			*
\***************************************************************************/

#include <system.h>
#include <vga.h>
#include <gui.h>
#include <memory.h>
#include <stdio.h>
#include <winman.h>
#include <string.h>
#include <LinkedList.hpp>

// a window
struct MattiseWindow {
	RECT pos; // position
	
	char* caption; // text in title bar
	
	uint_t repaint; // repaint or not
	
	MessageCallback cb; // callback function for messages
	
	uint_t bgcol; // color behind the window
	
	RECT invalrt; // invalidated rect - when repaint == 1
	
	bool includetitle; // include the title bar in the repaint (ie. start over)
	
	bool ischild; // child window?
	int parent; // if yes, the handle of the parent
	
};

// linked list of windows
CLinkedList<struct MattiseWindow*> WindowList;

// the desktop background
extern uchar_t* DesktopBG;

// initializes the window manager
void InitWinMan()
{
	// setup the first window (Desktop)
	struct MattiseWindow* DesktopWin = (struct MattiseWindow*) kmalloc( sizeof( struct MattiseWindow ) );
	DesktopWin->caption = (char*) kmalloc( 32 );
	strcpy( DesktopWin->caption, "Desktop" );
	DesktopWin->pos.x1 = 0; DesktopWin->pos.x2 = 800;
	DesktopWin->pos.y1 = 0; DesktopWin->pos.y2 = 600;
	DesktopWin->repaint = 1;
	DesktopWin->cb = (MessageCallback) NULL;
	DesktopWin->bgcol = RGB( 255, 255, 255 );
	
	// insert into the list
	WindowList.Push( DesktopWin );
}

// the internal painter - must be called each time
void InternalPainter( uint_t win )
{
	// get the handle
	struct MattiseWindow* thiswin = WindowList.Get(win);
	
	// draw the entire window again if we need to
	if( thiswin->includetitle && thiswin->repaint )
	{
		// draw it
		vga.DrawWindow( thiswin->pos.x1,
						thiswin->pos.y1,
						thiswin->pos.x2 - thiswin->pos.x1,
						thiswin->pos.y2 - thiswin->pos.y1,
						thiswin->caption,
						strlen( thiswin->caption )
						);
	}
	else if( thiswin->repaint )
	{
		// paint the invalidated rect
		vga.Rectangle(	thiswin->invalrt.x1,
						thiswin->invalrt.y1,
						thiswin->invalrt.x2 - thiswin->invalrt.x1,
						thiswin->invalrt.y2 - thiswin->invalrt.y1,
						thiswin->bgcol
						);
	}
}

// default callback
bool DefCallback( uint_t win, uint_t msg, uint_t param1, uint_t param2 )
{
	// what message - we do nothing yet
	switch( msg )
	{
		case WM_PAINT:

			break;
		
		case WM_MOUSEDOWN:
		
			break;
		
		default:
			
			break;
	};
}

// gives a window the focus (under an x,y position)
void GiveFocus( uint_t x, uint_t y )
{
	// find which window
	bool found = false;
	int win = 0;
	struct MattiseWindow* wnd = WindowList.Get(0);
	
	// check the rest
	int i = ( WindowList.Size() - 1 );
	while( i > 0 )
	{		
		// get the window
		wnd = WindowList.Get(i);
	
		// is the point inside this window?
		if( x > wnd->pos.x1 && x < wnd->pos.x2 )
		{
			if( y > wnd->pos.y1 && y < wnd->pos.y2 )
				found = true;
		}
		
		// check...
		if( found ) { break; };
		
		// otherwise keep going
		i--;
	}
	
	// check that one was found
	if( found )
	{		
		// repaint all...
		// TODO: improve efficiency here, only repaint the areas necessary
		WindowList.Get(0)->repaint = 1;
		
		// we have the pointer in wnd, index in i
		// delete the current one and push it onto the end
		WindowList.Delete( i );
		WindowList.Push( wnd );
		
		// done
		return;
	}
}

// handles a mousedown
void winman_MouseDown( uint_t but, uint_t mx, uint_t my )
{
	// give the focus to the window for now
	GiveFocus( mx, my );
}

// handles a repaint
void winman_Repaint()
{
	// running struct
	struct MattiseWindow* mywin = WindowList.Get( 0 );
	
	// repaint override
	bool repover = false;
	
	// check if we need to repaint this window
	if( mywin->repaint )
	{
		// paint the desktop
		//vga.DrawBitmap( (uchar_t*)(DesktopBG+54), 0, 0, 800, 600, 1 );
		vga.ClearScreen();
		
		// no longer
		mywin->repaint = 0;
		
		// we'll need to repaint
		repover = true;
	}
	
	// paint each next one
	int i = 1;
	while( i < WindowList.Size() )
	{
		// get this window
		mywin = WindowList.Get( i );
		
		// does this one need to be painted?
		if( repover || ( mywin->cb && mywin->repaint ) )
		{
			// modify if needs be
			if( repover )
			{
				mywin->includetitle = 1;
				mywin->repaint = 1;
			}
			
			// paint the window (these functions are selective in their painting)
			InternalPainter(i);
			mywin->cb( i, WM_PAINT, 0, 0 );
			
			// done painting!
			mywin->repaint = 0;
			mywin->includetitle = false;
		}
		
		/** would paint children here **/
		
		// increment i
		i++;
	}
}

// creates a new window
uint_t CreateWindow( uint_t flags, const char* title, uint_t x, uint_t y, uint_t w, uint_t h, MessageCallback cb )
{
	// here we go!
	struct MattiseWindow* wnd = (struct MattiseWindow*) kmalloc( sizeof( struct MattiseWindow ) );

	// the size of the window
	wnd->pos.x1 = x;
	wnd->pos.y1 = y;
	wnd->pos.x2 = x + w;
	wnd->pos.y2 = y + h;
	
	// it will need repainting
	wnd->repaint = 1;
	wnd->includetitle = true;
	
	// set the callback
	wnd->cb = cb;
	
	// set the caption
	wnd->caption = (char*) kmalloc( strlen( title ) + 1 );
	for( int i = 0; i < ( strlen( title ) + 1 ); i++ ) { wnd->caption[i] = 0; };
	strcpy( wnd->caption, title );
	
	// set the background color
	wnd->bgcol = RGB( 255, 255, 255 );
	
	// invalidate the window fully
	wnd->invalrt.x1 = 0;
	wnd->invalrt.y1 = 0;
	wnd->invalrt.x2 = w;
	wnd->invalrt.y2 = h;
	
	// insert it
	WindowList.Push( wnd );
	
	// return the index
	return WindowList.Size() - 1;
}
